﻿using System;
using System.Collections.Concurrent;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace SuperX.Common.Helper
{
    /// <summary>
    /// 反射帮助类
    /// </summary>    
    public static class ReflectionUtil
    {
        /// <summary>
        /// 通过路径获取值
        /// </summary>
        /// <param name="src"></param>
        /// <param name="propertyPath"></param>
        /// <returns></returns>
        public static object GetValueByPath(this object src, string propertyPath)
        {
            if (src == null || string.IsNullOrEmpty(propertyPath))
            {
                return null;
            }
            PropertyInfo propertyInfo = ReflectionUtil.GetPropertyInfo(src, propertyPath);
            if (propertyInfo != null)
            {
                return propertyInfo.GetValue(src, null);
            }
            string[] array = propertyPath.Split(new char[]
            {
                '.'
            });
            if (array.Length <= 1)
            {
                if (Regex.IsMatch(propertyPath, "\\[\\S{1,}\\]$"))
                {
                    string value = Regex.Match(propertyPath, "\\S{1,}(?=\\[\\S{0,}\\])").Value;
                    string text = Regex.Match(propertyPath, "(?<=\\[)\\S{1,}(?=\\])").Value.Replace("\"", "");
                    if (string.IsNullOrEmpty(value))
                    {
                        IList list = src as IList;
                        if (list != null)
                        {
                            int num;
                            int.TryParse(text, out num);
                            if (num < list.Count)
                            {
                                return list[num];
                            }
                        }
                        else
                        {
                            IDictionary dictionary = src as IDictionary;
                            if (dictionary != null && dictionary.Contains(text))
                            {
                                return dictionary[Convert.ChangeType(text, src.GetType().GetGenericArguments()[0])];
                            }
                        }
                    }
                    else
                    {
                        object valueByPath = src.GetValueByPath(value);
                        IList list2 = valueByPath as IList;
                        if (list2 != null)
                        {
                            int num2;
                            int.TryParse(text, out num2);
                            if (num2 < list2.Count)
                            {
                                return list2[num2];
                            }
                        }
                        else
                        {
                            IDictionary dictionary2 = valueByPath as IDictionary;
                            if (dictionary2 != null && dictionary2.Contains(text))
                            {
                                return dictionary2[Convert.ChangeType(text, valueByPath.GetType().GetGenericArguments()[0])];
                            }
                        }
                    }
                }
                return null;
            }
            string propertyPath2 = string.Join(".", array.Skip(1));
            if (src == null)
            {
                return null;
            }
            return src.GetValueByPath(array[0]).GetValueByPath(propertyPath2);
        }


        /// <summary>
        /// 通过路径设置属性
        /// </summary>
        /// <param name="src"></param>
        /// <param name="propertyPath"></param>
        /// <param name="value"></param>
        public static void SetValueByPath(this object src, string propertyPath, object value)
        {
            if (src == null)
            {
                return;
            }
            string[] propertyDepth = propertyPath.Split(new char[]
            {
                '.'
            });
            if (propertyDepth.Length > 1)
            {
                string propertyPath2 = string.Join(".", propertyDepth.Skip(1));
                if (src != null)
                {
                    src.GetValueByPath(propertyDepth[0]).SetValueByPath(propertyPath2, value);
                    return;
                }
            }
            else if (Regex.IsMatch(propertyPath, "\\[\\d{0,}\\]$")) //如果是数组的序号
            {
                string propertyName = Regex.Match(propertyPath, "\\S{1,}(?=\\[\\d{0,}\\])").Value;
                int num;
                int.TryParse(Regex.Match(propertyPath, "(?<=\\[)\\d{1,}(?=\\])").Value, out num);
                if (string.IsNullOrEmpty(propertyName))
                {
                    IList list = src as IList;
                    if (list != null && num < list.Count)
                    {
                        list[num] = value;
                        return;
                    }
                }
                else
                {
                    PropertyInfo propertyInfo = ReflectionUtil.GetPropertyInfo(src, propertyName);
                    IList list2 = ((propertyInfo != null) ? propertyInfo.GetValue(src, null) : null) as IList;
                    if (list2 != null && num < list2.Count)
                    {
                        list2[num] = value;
                        return;
                    }
                }
            }
            else 
            {
                if (src != null)
                {
                    GetPropertyInfo(src, propertyDepth[0])?.SetValue(src, value, null);
                }
            }
        }

        private static PropertyInfo GetPropertyInfo(object src, string propertyName)
        {
            if (src == null)
            {
                return null;
            }
            if (string.IsNullOrEmpty(propertyName))
            {
                return null;
            }
            return ReflectionUtil.GetPropertyInfo(src.GetType(), propertyName);
        }

        private static PropertyInfo GetPropertyInfo(Type type, string propertyName)
        {
            if (type == null)
            {
                return null;
            }
            if (string.IsNullOrEmpty(propertyName))
            {
                return null;
            }
            Dictionary<string, PropertyInfo> propertyInfos = ReflectionUtil.GetPropertyInfos(type);
            if (propertyInfos != null && propertyInfos.ContainsKey(propertyName))
            {
                return propertyInfos[propertyName];
            }
            return null;
        }

        internal static Dictionary<string, PropertyInfo> GetPropertyInfos(Type type)
        {
            Dictionary<string, PropertyInfo> dictionary = new Dictionary<string, PropertyInfo>();
            ReflectionUtil.propertyBuf.TryGetValue(type,out dictionary);
            if (dictionary == null)
            {
                IEnumerable<IGrouping<string, PropertyInfo>> enumerable = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToLookup((PropertyInfo t) => t.Name, (PropertyInfo t) => t);
                Dictionary<string, PropertyInfo> dictionary2 = new Dictionary<string, PropertyInfo>();
                foreach (IGrouping<string, PropertyInfo> grouping in enumerable)
                {
                    dictionary2.Add(grouping.Key, grouping.FirstOrDefault<PropertyInfo>());
                }
                ReflectionUtil.propertyBuf.TryAdd(type, dictionary2);
                ReflectionUtil.propertyBuf.TryGetValue(type, out dictionary);
            }
            return dictionary;
        }

        public static object ChangeType(object value, Type type)
        {
            Type type2 = type;
            object result;
            try
            {
                if (type2.IsGenericType && type2.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                {
                    if (value == null)
                    {
                        return null;
                    }
                    type2 = Nullable.GetUnderlyingType(type2);
                }
                if (value == null)
                {
                    result = null;
                }
                else if (type2 == value.GetType())
                {
                    result = value;
                }
                else if (type2.IsEnum)
                {
                    string text = Convert.ToString(value);
                    if (text.Length > 0)
                    {
                        result = Enum.Parse(type2, text, true);
                    }
                    else
                    {
                        result = null;
                    }
                }
                else if (Convert.ToString(value).Length > 0)
                {
                    result = Convert.ChangeType(value, type2);
                }
                else
                {
                    result = null;
                }
            }
            catch (Exception)
            {
                result = null;
            }
            return result;
        }

        /// <summary>
        /// 获取类型所有的直接继承的类型，对[抽象类]再递归获取
        /// <para>使用的话:用递归，遍历<see cref="ClassData.ChildrenTypes"/> 判断<see cref="Type.IsAbstract"/>进行递归</para>
        /// </summary>
        /// <param name="target">目标对象</param>
        /// <param name="inData"></param>
        public static void GetInheritors(Type target, ref ClassData inData, int layerindex = 0)
        {
            if (inData == null) return;

            //获取所有target的继承者,且只获取直接继承的类型
            var types = target.Assembly.GetTypes()
                .Where(a => target.IsAssignableFrom(a)
                            && a != target
                            && a.BaseType == target).ToList();
            inData.LayerIndex += layerindex == 0 ? 1 : layerindex;
            inData.ClassType = target;
            inData.Father = inData;
            //获取直接继承的类
            inData.ChildrenTypes = types.ToList();

            //获取所有抽象的继承类型
            var childrenType = types.Where(a => a.IsAbstract && a != target).ToList();
            if (childrenType.Count == 0)
            {
                return;
            }
            else
            {
                foreach (var type in childrenType)
                {
                    ClassData children = new ClassData()
                    {
                        Father = inData,
                        ClassType = type,
                    };
                    GetInheritors(type, ref children, inData.LayerIndex + 1);
                    inData.Children.Add(children);
                }
            }
        }

        public static Type[] GetAssembly(string path)
        {
            Assembly ass = Assembly.LoadFile(path);
            Type[] type = ass.GetTypes();
            return type;
        }

        private static readonly ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>> propertyBuf = new ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>>();
    }

    /// <summary>
    /// 类的数据
    /// <para>存入的是这个超类的下所有子类的类型数据</para>
    /// <para>以直接继承为条件,抽象类再分一次</para>
    /// </summary>
    public sealed class ClassData
    {
        /// <summary>
        /// 层级索引
        /// </summary>
        public int LayerIndex { get; set; }
        /// <summary>
        /// 当前类的类型
        /// </summary>
        public Type ClassType { get; set; }
        /// <summary>
        /// 当前类的父类
        /// </summary>
        public ClassData Father { get; set; }

        /// <summary>
        /// 所有再次抽象的类
        /// </summary>
        public List<ClassData> Children { get; set; } = new List<ClassData>();
        /// <summary>
        /// 当前类被直接继承的子类集合
        /// </summary>
        public List<Type> ChildrenTypes { get; set; }
    }
}
